package com.opensource.component.query.wrapper;

import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.opensource.component.query.exception.MpExtendException;
import com.opensource.component.query.matedata.AggregationColumn;
import com.opensource.component.query.matedata.TableMetadataHelper;
import com.opensource.component.query.script.AggregationFunction;
import com.opensource.component.query.script.ExtendMybatisCommand;
import com.opensource.component.query.sharding.TableShardingStrategy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import lombok.Getter;

//如下代码事例: 创建查询的wrapper
//   final LambdaExtendWrapper<MasterTable> wrapper = new LambdaExtendWrapper<>(MasterTable.class);
//   主表的查询条件
//   wrapper.eq(MasterTable::getId, 1)
//     进行 RIGHT JOIN 关联查询
//     .rightJoin(JoinTable.class,
//     JOIN ON的条件
//     jw ->jw.eq(JoinTable::getUserId, MasterTable::getId)
//       .or(sjw -> sjw.eq(JoinTable::getAmount, 100)
//       .eq(JoinTable::getUserId, MasterTable::getId))
//       将子表的查询字段放在查询列上(单表查询会自动过滤，不会查询)
//       .select(JoinTable::getAmount)
//       对子表进行自定义分库分表策略
//       .shardingStrategy(new JoinTableShardingStrategy()))
//     主表自定义分库分表策略
//     .shardingStrategy(new MasterTableShardingStrategy())
//     where条件进行子表条件(条件不会过滤，如果单表查询添加了则会直接报错)
//     .eq(JoinTable.class, JoinTable::getAmount, 100);

/**
 * @author ZonLen since on 2021/12/18 下午8:11
 * @see com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
 */
public abstract class AbstractExtendWrapper<T, Children extends AbstractExtendWrapper<T, Children>>
    extends AbstractLambdaWrapper<T, Children> implements
    Query<Children, T, SFunction<T, ?>> {

  /**
   * 判断当前是否使用过，每次都要重新创建wrapper对象，线程不安全
   */
  protected String currentStatementMethodName;

  /**
   * 当前表别名
   */
  protected String tableAlias;

  private TableShardingStrategy tableShardingStrategy;

  /**
   * 设置当前statementId
   */
  public void doSharingJoinOperate(String currentStatementMethodName) {
    if (StringUtils.isNotBlank(tableAlias)) {
      paramNameValuePairs.remove(tableAlias);
      this.tableAlias = null;
      innerWrappers.forEach(ew->ew.setTableAlias(null));
    }
    if (ExtendMybatisCommand.isExtendStatement(currentStatementMethodName)) {
      if (StringUtils.isNotBlank(this.currentStatementMethodName) && !ExtendMybatisCommand
          .isExtendStatement(this.currentStatementMethodName)) {
        throw new MpExtendException("不支持多表和单表复用Wrapper,请重新创建Wrapper");
      }
      setTableAlias(TableMetadataHelper.tableAlias(getEntityClass()));
      for (AbstractExtendWrapper<T, Children> innerWrapper : innerWrappers) {
        innerWrapper.setTableAlias(TableMetadataHelper.tableAlias(innerWrapper.getEntityClass()));
      }
      if (null != tableShardingStrategy) {
        this.finalTableName = TableMetadataHelper
            .finalTableName(getEntityClass(), tableShardingStrategy);
        if (StringUtils.isNotBlank(tableAlias)) {
          this.finalTableName = this.finalTableName + Constants.AS + tableAlias;
        }
      }
      paramNameValuePairs.put(tableAlias, finalTableName);
    } else {
      if (StringUtils.isNotBlank(this.currentStatementMethodName) && ExtendMybatisCommand
          .isExtendStatement(this.currentStatementMethodName)) {
        throw new MpExtendException("不支持多表和单表复用Wrapper,请重新创建");
      }
    }
    this.currentStatementMethodName = currentStatementMethodName;
  }

  /**
   * 表名
   */
  protected String finalTableName;

  /**
   * 查询字段片段
   */
  protected List<SharedString> selectColumnSegments = new ArrayList<>();

  protected List<AggregationColumn> aggregationSelectSegments = new ArrayList<>();

  public final List<AbstractExtendWrapper<T, Children>> innerWrappers = new ArrayList<>();


  protected AbstractExtendWrapper(Class<T> tableClass) {
    super.initNeed();
  }

  public void setTableAlias(String tableAlias) {
    this.tableAlias = tableAlias;
    this.finalTableName = TableMetadataHelper.globalFinalTableName(getEntityClass());
    if (StringUtils.isNotBlank(tableAlias)) {
      this.finalTableName = this.finalTableName + Constants.AS + tableAlias;
    }
  }

  /**
   * join 表添加到where条件中
   */
  @Getter
  private final List<List<ISqlSegment>> joinTableWhereCondition = new ArrayList<>();

  /**
   * 自定义分表策略，优先级高于@TableSharding注解方式
   */
  public Children shardingStrategy(TableShardingStrategy tableShardingStrategy) {
    if (tableShardingStrategy != null) {
      this.tableShardingStrategy = tableShardingStrategy;
    }
    return typedThis;
  }

  /**
   * 查询字段
   *
   * @see com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
   */
  @SafeVarargs
  @Override
  public final Children select(SFunction<T, ?>... columns) {
    if (ArrayUtils.isEmpty(columns)) {
      return typedThis;
    }
    if (this instanceof LambdaJoinWrapper) {
      Arrays.stream(columns).map(this::columnToString)
          .forEach(
              column -> ((LambdaJoinWrapper<?, T>) this).masterTableWrapper.addJoinSelect(column));
      return typedThis;
    }
    Arrays.stream(columns).map(this::columnToString)
        .forEach(column -> selectColumnSegments.add(new SharedString(column)));
    return typedThis;
  }

  /**
   * 考虑主表别名 聚合函数的查询 COUNT(xxx) AS total; SUM(xxx) AS totalAmount 等等
   */
  public final Children select(SFunction<T, ?> sFunction, String alias,
      AggregationFunction aggregationFunction) {
    String column = columnToString(sFunction);
    if (this instanceof LambdaJoinWrapper) {
      column = tableAlias + Constants.DOT + column;
      String aggregationColumn = aggregationFunction.format(column, alias);
      ((LambdaJoinWrapper<?, T>) this).masterTableWrapper.addJoinSelect(aggregationColumn);
      return typedThis;
    }
    aggregationSelectSegments.add(new AggregationColumn(column, aggregationFunction, alias));
    return typedThis;
  }

  /**
   * 查询字段
   *
   * @see com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
   */
  @Override
  public Children select(Class<T> tableClass, Predicate<TableFieldInfo> predicate) {
    final TableInfo tableInfo = TableInfoHelper.getTableInfo(tableClass);
    if (this instanceof LambdaJoinWrapper) {
      ((LambdaJoinWrapper<?, T>) this).masterTableWrapper.addJoinSelect(
          TableMetadataHelper.tableMetadata(tableClass).getSelectColumnsIgnorePk(predicate));
      return typedThis;
    }
    tableInfo.getFieldList().stream()
        .filter(TableFieldInfo::isSelect).filter(predicate == null ? a -> true : predicate)
        .map(TableFieldInfo::getSqlSelect)
        .map(SharedString::new).forEach(selectColumnSegments::add);
    return typedThis;
  }

  /**
   * 获取表名
   */
  public String getFinalTableName() {
    return finalTableName;
  }


  /**
   * 获取字段名
   */
  @Override
  protected String columnToString(SFunction<T, ?> column) {
    final String columnToString = super.columnToString(column, true);
    if (StringUtils.isNotBlank(tableAlias)) {
      return tableAlias + StringPool.DOT + columnToString;
//      return ExtendConstant.TABLE_ALIAS_COLUMN_PLACE_HOLDER + columnToString;
    }
    return columnToString;
  }


  @Override
  public void clear() {
    super.clear();
  }
}
